﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;


namespace EnterpriseLibraryGeneric.DataAccess
{
    class DataAccessApplicationBlock : IDataAccessComponentManager
    {
        private Microsoft.Practices.EnterpriseLibrary.Data.Database db;
        private DbCommand dbCommand;
        private DbConnection connection;
        private DbTransaction transaction;

        public DataAccessApplicationBlock(string dataProvider)
        {
            this.db = DatabaseFactory.CreateDatabase(dataProvider);
        }

        /// <summary>
        /// Ejecuta una acción sobre la base de datos
        /// </summary>
        /// <param name="commandToExecute">Nombre de Stored Procedure o Comando de texto a ejecutar</param>
        /// <param name="commandType">Indica si el commandToExecute es un Stores Procedure o comando de texto</param>
        /// <returns></returns>
        public int ExecuteNonQuery(string commandToExecute, CommandType commandType)
        {
            this.CreateCommand(commandToExecute, commandType);
            return this.ExecuteNonQuery();
        }

        /// <summary>
        /// Ejecuta una acción sobre la base de datos
        /// </summary>
        /// <param name="commandToExecute">Nombre de Stored Procedure o Comando de texto a ejecutar</param>
        /// <param name="commandType">Indica si el commandToExecute es un Stores Procedure o comando de texto</param>
        /// <param name="parameters">Parametros que utiliza el commanToExecute</param>
        /// <returns></returns>
        public int ExecuteNonQuery(string commandToExecute, CommandType commandType, IList<DataAccessParameter> parameters)
        {
            int iResult;

            this.CreateCommand(commandToExecute, commandType);
            this.CreateParameters(parameters);
            iResult = this.ExecuteNonQuery();

            //Le carga a la coleccion de parametros de salida en caso de existir los valores de retorno
            this.CreateOutParameters(parameters);

            return iResult;

        }

        /// <summary>
        /// Ejecuta una consulta sobre la base y retorna un dataset con el resultado del mismo
        /// </summary>
        /// <param name="commandToExecute">Nombre de Stored Procedure o Comando de texto a ejecutar</param>
        /// <param name="commandType">Indica si el commandToExecute es un Stores Procedure o comando de texto</param>
        /// <returns></returns>
        public DataSet ExecuteQuery(string commandToExecute, CommandType commandType)
        {
            this.CreateCommand(commandToExecute, commandType);

            return this.ExecuteQuery();

        }

        /// <summary>
        /// Ejecuta una consulta sobre la base y retorna un dataset con el resultado del mismo
        /// </summary>
        /// <param name="commandToExecute">Nombre de Stored Procedure o Comando de texto a ejecutar</param>
        /// <param name="commandType">Indica si el commandToExecute es un Stores Procedure o comando de texto</param>
        /// <param name="parameters">Parametros que utiliza el commanToExecute</param>
        /// <returns></returns>
        public DataSet ExecuteQuery(string commandToExecute, CommandType commandType, IList<DataAccessParameter> parameters)
        {
            this.CreateCommand(commandToExecute, commandType);

            this.CreateParameters(parameters);

            return this.ExecuteQuery();

        }

        /// <summary>
        /// Abre una transacción
        /// </summary>
        public void BeginTransaction()
        {
            this.connection = this.db.CreateConnection();
            this.connection.Open();
            this.transaction = this.connection.BeginTransaction();

        }

        /// <summary>
        /// Confirma Transacción
        /// </summary>
        public void CommitTransaction()
        {
            this.transaction.Commit();
            this.connection.Close();
        }

        /// <summary>
        /// Vuelve hacia atras una Transaccion
        /// </summary>
        public void RollBackTransaction()
        {
            this.transaction.Rollback();
            this.connection.Close();
        }

        #region "Private Functions"

        /// <summary>
        /// Crea un comando
        /// </summary>
        /// <param name="commandToExecute"></param>
        /// <param name="commandType"></param>
        private void CreateCommand(string commandToExecute, CommandType commandType)
        {
            if (commandType == CommandType.StoredProcedure)
            {
                this.dbCommand = this.db.GetStoredProcCommand(commandToExecute);
            }
            else
            {
                this.dbCommand = this.db.GetSqlStringCommand(commandToExecute);
            }
        }

        /// <summary>
        /// Crea los parametros
        /// </summary>
        /// <param name="parameters"></param>
        private void CreateParameters(IList<DataAccessParameter> parameters)
        {
            foreach (DataAccessParameter param in parameters)
            {
                if (param.ParameterDir != ParameterDirection.Output)
                {
                    this.db.AddInParameter(this.dbCommand, param.ParameterName, param.ParameterType, param.ParameterValue);
                }
                else if (!param.ParameterName.Equals("cur_OUT"))
                {
                    this.db.AddOutParameter(this.dbCommand, param.ParameterName, param.ParameterType, param.Size);
                }
            }
        }

        /// <summary>
        /// Ejecuta una acción sobre la base teniendo en cuenta si hay o no una transaccion creada.
        /// </summary>
        /// <returns></returns>
        private int ExecuteNonQuery()
        {

            if (this.transaction == null)
            {
                return this.db.ExecuteNonQuery(this.dbCommand);
            }
            else
            {
                return this.db.ExecuteNonQuery(this.dbCommand, this.transaction);
            }

        }

        /// <summary>
        /// Ejecuta una acción sobre la base teniendo en cuenta si hay o no una transaccion creada.
        /// </summary>
        /// <returns></returns>
        private DataSet ExecuteQuery()
        {

            if (this.transaction == null)
            {
                return this.db.ExecuteDataSet(dbCommand);
            }
            else
            {
                return this.db.ExecuteDataSet(dbCommand, this.transaction);
            }

        }

        /// <summary>
        /// Carga los valores de los parametros de salida.
        /// </summary>
        /// <param name="parameters"></param>
        private void CreateOutParameters(IList<DataAccessParameter> parameters)
        {
            foreach (DataAccessParameter param in parameters)
            {
                param.ParameterValue = this.db.GetParameterValue(this.dbCommand, param.ParameterName);
            }

        }

        #endregion
    }
}
